// 版权所有2009 Go作者。版权所有。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package strconv

import "errors"

// lower（c）是小写字母当且仅当
// c是该小写字母或等效的大写字母。
// 不用写c='x'| | c='x'，可以写更低的（c）='x'。
// 请注意，非字母中较低的可以生成其他非字母。
func lower(c byte) byte {
	return c | ('x' - 'X')
}

// ErrRange表示某个值超出目标类型的范围。
var ErrRange = errors.New("value out of range")

// ErrSyntax表示值的语法与目标类型不符。
var ErrSyntax = errors.New("invalid syntax")

// 数字错误记录转换失败。
type NumError struct {
	Func string // 失败的函数（ParseBool、ParseInt、ParseUint、ParseFloat、ParseComplex）
	Num  string // 输入
	Err  error  // 转换失败的原因（例如ErrRange、ErrSyntax等）
}

func (e *NumError) Error() string {
	return "strconv." + e.Func + ": " + "parsing " + Quote(e.Num) + ": " + e.Err.Error()
}

func (e *NumError) Unwrap() error { return e.Err }

func syntaxError(fn, str string) *NumError {
	return &NumError{fn, str, ErrSyntax}
}

func rangeError(fn, str string) *NumError {
	return &NumError{fn, str, ErrRange}
}

func baseError(fn, str string, base int) *NumError {
	return &NumError{fn, str, errors.New("invalid base " + Itoa(base))}
}

func bitSizeError(fn, str string, bitSize int) *NumError {
	return &NumError{fn, str, errors.New("invalid bit size " + Itoa(bitSize))}
}

const intSize = 32 << (^uint(0) >> 63)

// IntSize是int或uint值的位大小。
const IntSize = intSize

const maxUint64 = 1<<64 - 1

// ParseUint类似于ParseInt，但用于无符号数字。
// 
// 不允许使用符号前缀。
func ParseUint(s string, base int, bitSize int) (uint64, error) {
	const fnParseUint = "ParseUint"

	if s == "" {
		return 0, syntaxError(fnParseUint, s)
	}

	base0 := base == 0

	s0 := s
	switch {
	case 2 <= base && base <= 36:
		// 有效基数；无需操作

	case base == 0:
		// 查找八进制、十六进制前缀。
		base = 10
		if s[0] == '0' {
			switch {
			case len(s) >= 3 && lower(s[1]) == 'b':
				base = 2
				s = s[2:]
			case len(s) >= 3 && lower(s[1]) == 'o':
				base = 8
				s = s[2:]
			case len(s) >= 3 && lower(s[1]) == 'x':
				base = 16
				s = s[2:]
			default:
				base = 8
				s = s[1:]
			}
		}

	default:
		return 0, baseError(fnParseUint, s0, base)
	}

	if bitSize == 0 {
		bitSize = IntSize
	} else if bitSize < 0 || bitSize > 64 {
		return 0, bitSizeError(fnParseUint, s0, bitSize)
	}

	// 截止是截止*基>最大64的最小数字。
	// 在常见情况下使用编译时常量。
	var cutoff uint64
	switch base {
	case 10:
		cutoff = maxUint64/10 + 1
	case 16:
		cutoff = maxUint64/16 + 1
	default:
		cutoff = maxUint64/uint64(base) + 1
	}

	maxVal := uint64(1)<<uint(bitSize) - 1

	underscores := false
	var n uint64
	for _, c := range []byte(s) {
		var d byte
		switch {
		case c == '_' && base0:
			underscores = true
			continue
		case '0' <= c && c <= '9':
			d = c - '0'
		case 'a' <= lower(c) && lower(c) <= 'z':
			d = lower(c) - 'a' + 10
		default:
			return 0, syntaxError(fnParseUint, s0)
		}

		if d >= byte(base) {
			return 0, syntaxError(fnParseUint, s0)
		}

		if n >= cutoff {
			// n*基溢出
			return maxVal, rangeError(fnParseUint, s0)
		}
		n *= uint64(base)

		n1 := n + uint64(d)
		if n1 < n || n1 > maxVal {
			// n+d溢出
			return maxVal, rangeError(fnParseUint, s0)
		}
		n = n1
	}

	if underscores && !underscoreOK(s0) {
		return 0, syntaxError(fnParseUint, s0)
	}

	return n, nil
}

// ParseInt解释给定基（0，2到36）中的字符串s和
// 位大小（0到64）并返回相应的值i。
// 
// 字符串可以以前导符号“+”或“-”开头。
// 
// 如果基参数为0，则符号（如果存在）后字符串的
// 前缀将隐含真正的基：2表示“0b”，8表示“0”或“0o”，
// 16表示“0x”，否则为10。此外，仅对于参数基0，根据
// 下划线字符。
// 整型文字的Go语法定义，允许使用
// 
// bitSize参数指定结果必须适合的整数类型
// 。位大小0、8、16、32和64 
// 对应于int、int8、int16、int32和int64。
// 如果位大小小于0或大于64，则返回错误。
// 
// ParseInt返回的错误具有具体类型*numeror 
// 并包含err.Num=s。如果s为空或包含无效的
// 位，则err.err=ErrSyntax，返回值为0；
// 如果s对应的值不能用给定大小的
// 有符号整数表示，err.err=ErrRange，返回值为
// 相应位大小和符号的最大值整数。
func ParseInt(s string, base int, bitSize int) (i int64, err error) {
	const fnParseInt = "ParseInt"

	if s == "" {
		return 0, syntaxError(fnParseInt, s)
	}

	// 取下引导标志。
	s0 := s
	neg := false
	if s[0] == '+' {
		s = s[1:]
	} else if s[0] == '-' {
		neg = true
		s = s[1:]
	}

	// 转换无符号和检查范围。
	var un uint64
	un, err = ParseUint(s, base, bitSize)
	if err != nil && err.(*NumError).Err != ErrRange {
		err.(*NumError).Func = fnParseInt
		err.(*NumError).Num = s0
		return 0, err
	}

	if bitSize == 0 {
		bitSize = IntSize
	}

	cutoff := uint64(1 << uint(bitSize-1))
	if !neg && un >= cutoff {
		return int64(cutoff - 1), rangeError(fnParseInt, s0)
	}
	if neg && un > cutoff {
		return -int64(cutoff), rangeError(fnParseInt, s0)
	}
	n := int64(un)
	if neg {
		n = -n
	}
	return n, nil
}

// Atoi相当于ParseInt（s，10，0），转换为int类型。
func Atoi(s string) (int, error) {
	const fnAtoi = "Atoi"

	sLen := len(s)
	if intSize == 32 && (0 < sLen && sLen < 10) ||
		intSize == 64 && (0 < sLen && sLen < 19) {
		// 适合int类型的小整数的快速路径。
		s0 := s
		if s[0] == '-' || s[0] == '+' {
			s = s[1:]
			if len(s) < 1 {
				return 0, &NumError{fnAtoi, s0, ErrSyntax}
			}
		}

		n := 0
		for _, ch := range []byte(s) {
			ch -= '0'
			if ch > 9 {
				return 0, &NumError{fnAtoi, s0, ErrSyntax}
			}
			n = n*10 + int(ch)
		}
		if s0[0] == '-' {
			n = -n
		}
		return n, nil
	}

	// 无效、大或带下划线整数的慢路径。
	i64, err := ParseInt(s, 10, 0)
	if nerr, ok := err.(*NumError); ok {
		nerr.Func = fnAtoi
	}
	return int(i64), err
}

// 下划线OK报告s中的下划线是否允许。
// 在这个函数中检查它们可以让所有解析器简单地跳过它们。
// 下划线只能出现在数字之间或基本前缀和数字之间。
func underscoreOK(s string) bool {
	// saw跟踪我们看到的最后一个字符（类）：
	// ^表示数字的开头，
	// 0表示数字或基前缀，
	// u表示下划线，
	// ！以上都没有。
	saw := '^'
	i := 0

	// 可选符号。
	if len(s) >= 1 && (s[0] == '-' || s[0] == '+') {
		s = s[1:]
	}

	// 可选的基前缀。
	hex := false
	if len(s) >= 2 && s[0] == '0' && (lower(s[1]) == 'b' || lower(s[1]) == 'o' || lower(s[1]) == 'x') {
		i = 2
		saw = '0' // 基本前缀计算为“下划线作为数字分隔符”的数字
		hex = lower(s[1]) == 'x'
	}

	// 正确数字。
	for ; i < len(s); i++ {
		// 数字总是可以的。
		if '0' <= s[i] && s[i] <= '9' || hex && 'a' <= lower(s[i]) && lower(s[i]) <= 'f' {
			saw = '0'
			continue
		}
		// 数字后必须加下划线。
		if s[i] == '_' {
			if saw != '0' {
				return false
			}
			saw = '_'
			continue
		}
		// 下划线后面还必须跟数字。
		if saw == '_' {
			return false
		}
		// 锯非数字，非下划线。
		saw = '!'
	}
	return saw != '_'
}
